package ai.esc.speedy.common.base;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;

/**
 * 通用消息返回对象
 *
 * @author SAGE
 * @version ...
 * v1 2019/4/3 20:04;
 * v2 2019/12/24 10:09
 */
@Slf4j
@Getter
@ToString
@EqualsAndHashCode
@SuppressWarnings("ALL")
public class Message implements Serializable {

    private Object data;
    @JsonIgnore
    private Boolean isRedirect;
    private Map<String, Object> meta;

    public Message(Builder builder) {
        this.data = builder.data;
        this.isRedirect = builder.isRedirect;
        this.meta = builder.meta;
    }

    public Message put(String key, Object value) throws IllegalArgumentException {

        if (data instanceof Map) {
            ((Map) data).put(key, value);
            return this;
        }

        if (data == null) {
            data = new HashMap<>(16);
            ((Map) data).put(key, value);
            return this;
        }

        throw new IllegalArgumentException("Data type is" + data.getClass());
    }

    /**
     * 获取Data
     * <p>
     * 常用于远程调用时客户端获取数据
     *
     * @return
     */
    public <T> T getData(Class<T> clazz) throws IllegalArgumentException {

        Class<Map> mapClass = Map.class;
        Class<?> aClass = clazz.getClass();
        // 本身就是Map数据，不转换
        if ((aClass != mapClass) && data instanceof Map) {
            return JSONObject.parseObject(JSONObject.toJSON(data).toString(), clazz);
        } else {
            return (T) data;
        }
    }

    /**
     * 获取请求状态
     *
     * @return
     */
    @JsonIgnore
    public int code() throws IllegalArgumentException {
        return Integer.parseInt(String.valueOf(this.meta.get("code")));
    }

    /**
     * 获取Data的JSON格式数据（常用于远程调用时客户端获取数据）
     *
     * @return
     */
    @JsonIgnore
    public JSONObject data2Json() throws IllegalArgumentException {
        return data instanceof Map ? JSONObject.parseObject(JSONObject.toJSON(data).toString()) : null;
    }

    /**
     * 将当前Message转为JSON格式字符串
     *
     * @return java.lang.String
     * @author zhougm
     * @version 2019/9/2 15:26
     */
    @JsonIgnore
    public String toJsonStr() {
        return JSON.toJSONString(this);
    }


    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {

        private Object data;
        private Boolean isRedirect = false;
        private Map<String, Object> meta = new HashMap<>(16);

        public Builder ok() {
            this.status(20000, "success!", Boolean.TRUE);
            return this;
        }

        public Builder ok(String msg) {
            this.status(20000, msg, Boolean.TRUE);
            return this;
        }

        public Builder ok(int code, String msg) {
            this.status(code, msg, Boolean.TRUE);
            return this;
        }

        public Builder warn(String msg) {
            this.status(30000, msg, "warn");
            log.warn(msg);
            return this;
        }

        public Builder warn(int code, String msg) {
            this.status(code, msg, "warn");
            log.warn(msg);
            return this;
        }

        public Builder error(String msg) {
            this.status(50000, msg, Boolean.FALSE);
            log.error(msg);
            return this;
        }

        public Builder error(String msg, Exception e) {
            this.status(50000, msg, Boolean.FALSE);
            this.data(e.getMessage());
            log.error(msg, e);
            return this;
        }

        public Builder error(int code, String msg, Exception e) {
            this.status(code, msg, Boolean.FALSE);
            this.data(e.getMessage());
            log.error(msg, e);
            return this;
        }

        private Builder status(int code, String msg, Object status) {
            this.meta.put("code", code);
            this.meta.put("message", msg);
            this.meta.put("status", status);
            this.meta.put("timestamp", new Timestamp(System.currentTimeMillis()));
            return this;
        }

        public Builder data(Object data) {
            this.data = data;
            return this;
        }

        public Builder put(String key, Object value) {

            if (this.data instanceof Map) {
                ((Map) data).put(key, value);
            }

            if (this.data == null) {
                this.data = new HashMap<>(16);
                ((Map) this.data).put(key, value);
            } else if (!(this.data instanceof Map)) {
                throw new IllegalArgumentException("Data type is" + data.getClass());
            }
            return this;
        }

        public Builder meta(Map<String, Object> meta) {
            this.meta = meta;
            return this;
        }

        public Builder isRedirect(boolean isRedirect) {
            this.isRedirect = isRedirect;
            return this;
        }

        public Message build() {
            return new Message(this);
        }

    }

}
